
#include <sstream>

#include "bocat/common/base64.hpp"

#include "aubin.hpp"

namespace   confab   {

Aubin::Aubin( const std::string& data_root, const std::string& index_file, DbEnv* dbenv_p, u_int32_t n_tables )
        : _data_root( data_root ),
		  _dbenv_p( dbenv_p ),
		  _bin_index( index_file, dbenv_p, n_tables ),
		  _filecell( data_root )
{
}

Aubin::~Aubin()
{
}

///////////////////////////////////////////////////////////////////////
//


int 
Aubin::insert( const std::string& kpin,
         const std::string& key,
         const std::string& data )
{
	boost::mutex::scoped_lock   _scoped_lock( _mutex );

    // STEP. check if key exists
	std::string	skey = getSkeyByKpin( kpin );
	RecordLocation	loc;
	try {
		_bin_index.get( kpin, skey, loc );
	}
	catch( std::exception& e ) {
		std::ostringstream	msg;
		msg << "fail to check existence for key '" << key << "' -- " << e.what();
		throw std::runtime_error( msg.str() );
	}

    // STEP. if key exists, delete the old one
	std::string	skey_b64 = bocat::common::base64_encode( skey );
	std::string kpin_b64 = bocat::common::base64_encode( kpin );

	if ( !loc.is_null() ) {
		_filecell.erase( skey_b64, kpin_b64, key, loc );
	}

    // STEP. insert the record
	//       . put data into file cell
	//       . put location into index
	loc.clear();
	_filecell.put( skey_b64, kpin_b64, key, data, loc );
	_bin_index.put( kpin, skey, loc );

    return  0;
}


int 
Aubin::insertIgnore( const std::string& kpin,
         const std::string& key,
         const std::string& data )
{
	boost::mutex::scoped_lock   _scoped_lock( _mutex );

    // STEP. check if key exists
	std::string	skey = getSkeyByKpin( kpin );
	RecordLocation	loc;
	try {
		_bin_index.get( kpin, skey, loc );
	}
	catch( std::exception& e ) {
		std::ostringstream	msg;
		msg << "fail to check existence for key '" << key << "' -- " << e.what();
		throw std::runtime_error( msg.str() );
	}

    // STEP. if key exists, ignore it
	if ( !loc.is_null() ) {
		return 0;
	}

    // STEP. insert the record
	//       . put data into file cell
	//       . put location into index
	std::string	skey_b64 = bocat::common::base64_encode( skey );
	std::string kpin_b64 = bocat::common::base64_encode( kpin );

	loc.clear();
	_filecell.put( skey_b64, kpin_b64, key, data, loc );
	_bin_index.put( kpin, skey, loc );


    return  0;
}


int 
Aubin::find( const std::string& kpin,
         const std::string& key,
               std::string& data )
{   
	boost::mutex::scoped_lock   _scoped_lock( _mutex_read );

    // STEP. check if key exists
	std::string	skey = getSkeyByKpin( kpin );
	RecordLocation	loc;
	try {
		_bin_index.get( kpin, skey, loc );
	}
	catch( std::exception& e ) {
		std::ostringstream	msg;
		msg << "fail to check existence for key '" << key << "' -- " << e.what();
		throw std::runtime_error( msg.str() );
	}

    // STEP. if key does NOT exist, just return
	if ( loc.is_null() ) {
		return 0;
	}

    // STEP. get the record
	//       . get data from file cell
	std::string	skey_b64 = bocat::common::base64_encode( skey );
	std::string kpin_b64 = bocat::common::base64_encode( kpin );

	int	data_size = _filecell.get( skey_b64, kpin_b64, key, data, loc );

    return  data_size;
}


int 
Aubin::erase( const std::string& kpin,
           const std::string& key )
{
	boost::mutex::scoped_lock   _scoped_lock( _mutex );

    // STEP. check if key exists
	std::string	skey = getSkeyByKpin( kpin );
	RecordLocation	loc;
	try {
		_bin_index.get( kpin, skey, loc );
	}
	catch( std::exception& e ) {
		std::ostringstream	msg;
		msg << "fail to check existence for key '" << key << "' -- " << e.what();
		throw std::runtime_error( msg.str() );
	}

    // STEP. if key does NOT exist, just return
	if ( loc.is_null() ) {
		return 0;
	}

    // STEP. erase the record
	//       . erase data from file cell
	//       . erase index from file cell
	std::string	skey_b64 = bocat::common::base64_encode( skey );
	std::string kpin_b64 = bocat::common::base64_encode( kpin );

	_filecell.erase( skey_b64, kpin_b64, key, loc );
	_bin_index.erase( kpin, skey );

    return  0;
}


///////////////////////////////////////////////////////////////////////
//


const std::string   
Aubin::getSkeyByKpin( const std::string& kpin )
{
	char	skey[12];
	u_int32_t	skey_size;

	memset(skey, 0, sizeof(skey));
	skey_size = (kpin.size() > sizeof(skey)) ? sizeof(skey) : kpin.size();
	memcpy( skey, kpin.c_str() + (kpin.size() - skey_size), skey_size );

	return	std::string( skey, skey_size );
}


///////////////////////////////////////////////////////////////////////
//


}   //// namespace confab

